package com.yykj.user.center.server.service.impl.patient;

import com.alibaba.fastjson.JSON;
import com.yykj.common.enums.ResultEnums;
import com.yykj.common.exception.NormalException;
import com.yykj.common.service.RedisService;
import com.yykj.common.util.GsonUtil;
import com.yykj.common.util.LocalRequestUtil;
import com.yykj.common.util.LocalStringUtil;
import com.yykj.common.vo.PageData;
import com.yykj.hospital.client.dto.DoctorDTO;
import com.yykj.hospital.client.dto.HospitalConfigInfo;
import com.yykj.hospital.client.dto.HospitalSettingAppointmentDTO;
import com.yykj.hospital.client.dto.banci.DoctorAvailableAppointmentDTO;
import com.yykj.hospital.client.feign.HospitalServerFeignClient;
import com.yykj.hospital.client.form.banci.AppointmentOrderForm;
import com.yykj.message.center.client.feign.MessageFeignClient;
import com.yykj.user.center.server.constant.Constant;
import com.yykj.user.center.server.dto.RegisteredOrderDTO;
import com.yykj.user.center.server.dto.UserLoginInfo;
import com.yykj.user.center.server.emun.OrderType;
import com.yykj.user.center.server.form.AppointmentForm;
import com.yykj.user.center.server.form.CancelAppointmentForm;
import com.yykj.user.center.server.model.Patient;
import com.yykj.user.center.server.model.PatientCardInfo;
import com.yykj.user.center.server.model.RegisteredOrder;
import com.yykj.user.center.server.repository.patient.PatientCardInfoRepository;
import com.yykj.user.center.server.repository.patient.PatientRepository;
import com.yykj.user.center.server.repository.patient.RegisterOrderRepository;
import com.yykj.user.center.server.service.patient.RegisterService;
import com.yykj.user.center.server.utils.DateUtil;
import com.yykj.user.center.server.utils.OrderUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * @author chenl
 * @version 1.0
 * @date 2021/11/3 19:32
 */
@Service
@Slf4j
public class RegisterServiceImpl implements RegisterService {
    @Autowired
    private HospitalServerFeignClient hospitalServerFeignClient;
    @Autowired
    private RedisService redisService;
    @Autowired
    private PatientCardInfoRepository patientCardInfoRepository;
    @Autowired
    private RegisterOrderRepository registerOrderRepository;
    @Autowired
    private MessageFeignClient messageFeignClient;
    @Autowired
    private PatientRepository patientRepository;

    public void test(){
        AppointmentOrderForm form = new AppointmentOrderForm();
        form.setDoctorId("01b6ab8f03474985ada80522445c5706");
        form.setCnt(1);
        form.setAppointId(126);
        DoctorAvailableAppointmentDTO doctorAvailableAppointmentDTO = hospitalServerFeignClient.changeAvailableAppointmentCnt(form);
        if(doctorAvailableAppointmentDTO.getRemain() ==0){
            throw new NormalException(ResultEnums.NO_TICKET_ERROR);
        }
    }

    @Override
    public Boolean create(AppointmentForm appointmentForm) {
        Integer hospitalId = appointmentForm.getHospitalId();
        String patientCardId = appointmentForm.getPatientCardId();
        String date = appointmentForm.getDate();
        Integer deptId = appointmentForm.getDeptId();
        check(hospitalId,patientCardId,date,deptId);
        AppointmentOrderForm form = new AppointmentOrderForm();
        form.setDoctorId(appointmentForm.getDoctorId());
        form.setCnt(1);
        form.setAppointId(appointmentForm.getAppointId());
        Long time = System.currentTimeMillis() + Constant.TIMEOUT;
        if(!redisService.lock(appointmentForm.getAppointId().toString(), String.valueOf(time))){
            throw new NormalException(ResultEnums.PUSH_PURCHASE_FAILED);
        }
        DoctorAvailableAppointmentDTO doctorAvailableAppointmentDTO = hospitalServerFeignClient.changeAvailableAppointmentCnt(form);
        log.info("doctorAvailableAppointmentDTO is===========>{}", JSON.toJSONString(doctorAvailableAppointmentDTO));
        if(doctorAvailableAppointmentDTO.getRemain().intValue() < 0){
                    throw new NormalException(ResultEnums.NO_TICKET_ERROR);
        }
        String id = LocalStringUtil.getUUID();
        PatientCardInfo patientCardInfo = patientCardInfoRepository.findById(patientCardId).orElse(null);
        String token = LocalRequestUtil.getToken();
        UserLoginInfo userLoginInfo = GsonUtil.fromJson(redisService.get(token),UserLoginInfo.class);
        String accountId = userLoginInfo.getUserId();
        RegisteredOrder registeredOrder = new RegisteredOrder();
        BeanUtils.copyProperties(appointmentForm,registeredOrder);
        String strStartDateTime = appointmentForm.getStartDatetime();
        String strEndDateTime = appointmentForm.getEndDatetime();
        String strStartTime = date+" "+strStartDateTime+":00";
        String strEndTime = date+" "+strEndDateTime+":00";
        registeredOrder.setIdCard(id);
        registeredOrder.setNormalOrExpert(appointmentForm.getNormalOrExpert());
        registeredOrder.setId(LocalStringUtil.getUUID());
        registeredOrder.setAccountId(accountId);
        registeredOrder.setIdCard(patientCardInfo==null?"":patientCardInfo.getIdCard());
        registeredOrder.setOrderNo(OrderUtil.getOrderNo(hospitalId, OrderType.YU_YUE_GUA_HAO.getCode()));
        registeredOrder.setStartDatetime(DateUtil.stringDateTime2DateTime(strStartTime));
        registeredOrder.setEndDatetime(DateUtil.stringDateTime2DateTime(strEndTime));
        registeredOrder.setState(1);
        registeredOrder.setPatientCardNo(patientCardInfo.getCardNo());
        registerOrderRepository.save(registeredOrder);
        redisService.unlock(appointmentForm.getAppointId().toString(), String.valueOf(time));

        //sms
        Optional<Patient> optional = this.patientRepository.findById(registeredOrder.getAccountId());
        if(optional.isPresent()){
            LocalDateTime startDatetime = registeredOrder.getStartDatetime();
            String dateStr = startDatetime.getYear() + "年" +  startDatetime.getMonthValue() + "月" + startDatetime.getDayOfMonth() + "日 " + registeredOrder.getMorningOrAfternoon() + " " + startDatetime.getHour() + "点";
            if(startDatetime.getMinute() != 0 ){
                dateStr += startDatetime.getMinute() + "分";
            }


            messageFeignClient.smsPatientAppointment(optional.get().getMobile(),registeredOrder.getDoctorName(), dateStr,registeredOrder.getNormalOrExpert());
        }
        /*DoctorDTO doctorDTO = this.hospitalServerFeignClient.info(registeredOrder.getDoctorId());
        if(doctorDTO != null && StringUtils.isNotBlank(doctorDTO.getMobile())){
            String msg = date.replace("-","年").replace("-","月") + "日 " + appointmentForm.getMorningOrAfternoon() + " " + strStartDateTime;
            messageFeignClient.smsDoctorAppointment(msg, doctorDTO.getMobile());
        }*/
        return true;
    }

    public void check(Integer hospitalId,String patientCardId,String date,Integer deptId){
        HospitalConfigInfo hospitalConfigInfo = hospitalServerFeignClient.hospitalConfigInfo(hospitalId);
        HospitalSettingAppointmentDTO hospitalSettingAppointmentDTO = hospitalConfigInfo.getSettingAppointment();
        Integer cnt3 = hospitalSettingAppointmentDTO.getCnt3();//可预约最长时间:单位天，默认30
        Integer cnt4 = hospitalSettingAppointmentDTO.getCnt4();//一个就诊号一天最多可预约科室：默认2
        Integer cnt5 = hospitalSettingAppointmentDTO.getCnt5();//同一科室可预约号源：默认1
        Integer cnt6 = hospitalSettingAppointmentDTO.getCnt6();//一周内最多可约号源：默认7
        LocalDate localDate = DateUtil.stringDate2Date(date);
        Integer count = registerOrderRepository.countAllByPatientCardIdAndDate(patientCardId,localDate);
        if(count.intValue()==cnt4.intValue()){
            throw new NormalException(ResultEnums.LIMIT_APPONTMENT_ERROR);
        }
        count = registerOrderRepository.countAllByDeptIdAndDate(deptId,localDate);
        if(count.intValue()==cnt5.intValue()){
            throw new NormalException(ResultEnums.LIMIT_APPONTMENT_DEPT_ERROR);
        }
        LocalDate localDate1 = LocalDate.now().minusDays(7);
        count = registerOrderRepository.countAllByPatientCardIdAndDateBetween(patientCardId,localDate1,localDate);
        if(count.intValue()==cnt6.intValue()){
            throw new NormalException(ResultEnums.LIMIT_WEEK_ERROR);
        }
        localDate1 =localDate.plusDays(cnt3);
        if(localDate1.compareTo(localDate)<0){
            throw new NormalException(ResultEnums.LIMIT_DATE_ERROR);
        }
    }


    public Boolean complete(){
        log.info("定时任务开始======================>");
        LocalDate localDate = LocalDate.now();
        List<RegisteredOrder> registeredOrders = registerOrderRepository.findAllByStateAndDateBefore(1,localDate);
        List<RegisteredOrder> orders = new ArrayList<>();
        for(RegisteredOrder registeredOrder:registeredOrders){
            registeredOrder.setState(2);
            orders.add(registeredOrder);
        }
        registerOrderRepository.saveAll(orders);
        log.info("定时任务结束======================>");
        return true;
    }

    @Override
    public Boolean cancel(CancelAppointmentForm cancelAppointmentForm) {

        //先进行一系列校验
        RegisteredOrder registeredOrder = registerOrderRepository.findById(cancelAppointmentForm.getId()).orElse(null);
        if(registeredOrder==null){
            throw new NormalException(ResultEnums.NO_EXIST_ERROR);
        }
        registeredOrder.setState(3);
        registerOrderRepository.save(registeredOrder);
        //sms
        Optional<Patient> optional = this.patientRepository.findById(registeredOrder.getAccountId());
        if(optional.isPresent()){
            LocalDateTime startDatetime = registeredOrder.getStartDatetime();
            String dateStr = startDatetime.getYear() + "年" +  startDatetime.getMonthValue() + "月" + startDatetime.getDayOfMonth() + "日 " + registeredOrder.getMorningOrAfternoon() + " " + startDatetime.getHour() + "点";
            if(startDatetime.getMinute() != 0 ){
                dateStr += startDatetime.getMinute() + "分";
            }

            messageFeignClient.smsPatientAppointmentCancel(optional.get().getMobile(),registeredOrder.getDoctorName(), dateStr,registeredOrder.getNormalOrExpert());
        }
        /*DoctorDTO doctorDTO = this.hospitalServerFeignClient.info(registeredOrder.getDoctorId());
        if(doctorDTO != null && StringUtils.isNotBlank(doctorDTO.getMobile())){
            LocalDateTime startDatetime = registeredOrder.getStartDatetime();
            String msg = startDatetime.getYear() + "年" +  startDatetime.getMonthValue() + "月" + startDatetime.getDayOfMonth() + "日 " + registeredOrder.getMorningOrAfternoon() + " " + startDatetime.getHour() + ":" + startDatetime.getMinute();
            messageFeignClient.smsDoctorAppointmentCancel(msg, doctorDTO.getMobile());
        }*/
        return true;
    }

    @Override
    public PageData<RegisteredOrderDTO> list(Integer current, Integer size, String startDate, String endDate, String patientName, String doctorName, String patientCardId, Integer state, Integer hospitalId) {
        String token = LocalRequestUtil.getToken();
        UserLoginInfo userLoginInfo = GsonUtil.fromJson(redisService.get(token),UserLoginInfo.class);
        String userId = userLoginInfo.getUserId();
        if(current>=1){
            current -=1;
        }
        Pageable pageable = PageRequest.of(current, size, Sort.by(Sort.Order.desc("createTime")));
        Page<RegisteredOrder> registeredOrders =  registerOrderRepository.findAll(new Specification<RegisteredOrder>() {
            @Override
            public Predicate toPredicate(Root<RegisteredOrder> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                if(hospitalId!=null){
                    predicateList.add(builder.equal(root.get("hospitalId"), hospitalId));
                }else{
                    predicateList.add(builder.equal(root.get("accountId"), userId));
                }
                if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
                    LocalDate start = DateUtil.stringDate2Date(startDate);
                    LocalDate end = DateUtil.stringDate2Date(endDate);
                    predicateList.add(builder.between(root.get("createTime"), start, end));
                }
                if (StringUtils.isNotBlank(patientName)) {
                    predicateList.add(builder.like(root.get("patientName"), patientName));
                }

                if (StringUtils.isNotBlank(doctorName)) {
                    predicateList.add(builder.like(root.get("doctorName"), doctorName));
                }

                if (StringUtils.isNotBlank(patientCardId)) {
                    predicateList.add(builder.equal(root.get("patientCardId"), patientCardId));
                }

                if (state != null) {
                    predicateList.add(builder.equal(root.get("state"), state));
                }


                Predicate[] predicates = new Predicate[predicateList.size()];
                query.where(predicateList.toArray(predicates));
                return query.getRestriction();
            }
        }, pageable);
        List<RegisteredOrderDTO> registeredOrderDTOS = new ArrayList<>();

        for(RegisteredOrder registeredOrder:registeredOrders.getContent()){
            RegisteredOrderDTO registeredOrderDTO = new RegisteredOrderDTO();
            BeanUtils.copyProperties(registeredOrder,registeredOrderDTO);
            String doctorId = registeredOrderDTO.getDoctorId();
            DoctorDTO doctorDTO = hospitalServerFeignClient.info(doctorId);
            if(doctorDTO != null){
                PatientCardInfo patientCardInfo = patientCardInfoRepository.findById(registeredOrder.getPatientCardId()).orElse(null);
                registeredOrderDTO.setRelation(patientCardInfo==null?"":patientCardInfo.getRelation());
                registeredOrderDTO.setAvatar(doctorDTO.getHeadImg());
                registeredOrderDTO.setOfficer(doctorDTO.getOfficer());
                registeredOrderDTOS.add(registeredOrderDTO);
            }

        }
        PageData page = new PageData();
        page.setList(registeredOrderDTOS);
        page.setCurrentPage(current+1);
        page.setPageSize(size);
        page.setTotalCount(registeredOrders.getTotalElements());
        return page;
    }

    @Override
    public PageData<RegisteredOrderDTO> adminList(Integer current, Integer size, String startDate, String endDate, String patientName, String doctorName, String patientCardId, Integer state, Integer hospitalId) {
        if(current>=1){
            current -=1;
        }
        Pageable pageable = PageRequest.of(current, size, Sort.by(Sort.Order.desc("createTime")));
        Page<RegisteredOrder> registeredOrders =  registerOrderRepository.findAll(new Specification<RegisteredOrder>() {
            @Override
            public Predicate toPredicate(Root<RegisteredOrder> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
                List<Predicate> predicateList = new ArrayList<>();
                if(hospitalId!=null){
                    predicateList.add(builder.equal(root.get("hospitalId"), hospitalId));
                }
                if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
                    LocalDate start = DateUtil.stringDate2Date(startDate);
                    LocalDate end = DateUtil.stringDate2Date(endDate);
                    predicateList.add(builder.between(root.get("createTime"), start, end));
                }
                if (StringUtils.isNotBlank(patientName)) {
                    predicateList.add(builder.like(root.get("patientName"), patientName));
                }

                if (StringUtils.isNotBlank(doctorName)) {
                    predicateList.add(builder.like(root.get("doctorName"), doctorName));
                }

                if (StringUtils.isNotBlank(patientCardId)) {
                    predicateList.add(builder.equal(root.get("patientCardId"), patientCardId));
                }

                if (state != null) {
                    predicateList.add(builder.equal(root.get("state"), state));
                }

                Predicate[] predicates = new Predicate[predicateList.size()];
                query.where(predicateList.toArray(predicates));
                return query.getRestriction();
            }
        }, pageable);
        List<RegisteredOrderDTO> registeredOrderDTOS = new ArrayList<>();
        for(RegisteredOrder registeredOrder:registeredOrders.getContent()){
            RegisteredOrderDTO registeredOrderDTO = new RegisteredOrderDTO();
            BeanUtils.copyProperties(registeredOrder,registeredOrderDTO);
            String doctorId = registeredOrderDTO.getDoctorId();
            DoctorDTO doctorDTO = hospitalServerFeignClient.info(doctorId);
            if(doctorDTO != null){
                PatientCardInfo patientCardInfo = patientCardInfoRepository.findById(registeredOrder.getPatientCardId()).orElse(null);
                registeredOrderDTO.setRelation(patientCardInfo==null?"":patientCardInfo.getRelation());
                registeredOrderDTO.setAvatar(doctorDTO.getHeadImg());
                registeredOrderDTO.setOfficer(doctorDTO.getOfficer());
                registeredOrderDTOS.add(registeredOrderDTO);
            }

        }
        PageData page = new PageData();
        page.setList(registeredOrderDTOS);
        page.setCurrentPage(current+1);
        page.setPageSize(size);
        page.setTotalCount(registeredOrders.getTotalElements());
        return page;
    }

}
